﻿/**********************************************************************
 * Copyright (c) 2008, j. montgomery                                  *
 * All rights reserved.                                               *
 *                                                                    *
 * Redistribution and use in source and binary forms, with or without *
 * modification, are permitted provided that the following conditions *
 * are met:                                                           *
 *                                                                    *
 * + Redistributions of source code must retain the above copyright   *
 *   notice, this list of conditions and the following disclaimer.    *
 *                                                                    *
 * + Redistributions in binary form must reproduce the above copyright*
 *   notice, this list of conditions and the following disclaimer     *
 *   in the documentation and/or other materials provided with the    *
 *   distribution.                                                    *
 *                                                                    *
 * + Neither the name of j. montgomery's employer nor the names of    *
 *   its contributors may be used to endorse or promote products      *
 *   derived from this software without specific prior written        *
 *   permission.                                                      *
 *                                                                    *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  *
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS  *
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE     *
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES           *
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)      *
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
 * OF THE POSSIBILITY OF SUCH DAMAGE.                                 *
 **********************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;

using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Threading;
using UpdateDnsOMatic.Enums;
using UpdateDnsOMatic.Configuration;

namespace UpdateDnsOMatic.Service
{
    public struct StateInfo
    {
        public DnsOMaticClient DnsUpdateClient;
        public AutoResetEvent ResetEvent;
    }

    partial class UpdateDnsOMaticService : ServiceBase
    {
        private const int _timerInterval = 10000;
        private Thread _updateOMaticThread;
        private DnsOMaticClient _dnsOMaticClient;
        private AutoResetEvent _autoEvent = new AutoResetEvent(false);
        private System.Threading.Timer _timer;
        private System.Diagnostics.Stopwatch _watch;
        private bool _isRunning = false;
        private bool _shutDown = false;
        // used to run the update immediately when the service starts
        private bool _firstRun = false;
        public bool IsRunning
        {
            get { return _isRunning; }
        }

        public UpdateDnsOMaticService()
        {
            InitializeComponent();
        }

        // Non-"Windows Service" entry point - used mainly for debugging.
        public void Start(string[] args)
        {
            this.OnStart(args);

            // Once started, we block until Client Thread is shutdown or crashes
            _updateOMaticThread.Join();

            Trace.WriteLine("DnsOMatic Update thread has ended.");
        }

        protected override void OnStart(string[] args)
        {
            // Start up the log submission thread.
            _updateOMaticThread = new Thread(new ThreadStart(this.RunUpdateDnsOMaticThreadController));
            _updateOMaticThread.Start();

            // Wait until _updateOMaticThread is created or started
            while (_updateOMaticThread == null || !_updateOMaticThread.IsAlive)
                Thread.Sleep(1000);

            Trace.WriteLine("Dns-O-Matic Update Thread started.");
        }

        private void RunUpdateDnsOMaticThreadController()
        {
            _isRunning = true;
            _firstRun = true;
            Trace.WriteLine("Starting DnsOMatic Update Thread.");

            // Create LogConverter
            _dnsOMaticClient = new DnsOMaticClient();
            // Setup the StopWatch
            _watch = new System.Diagnostics.Stopwatch();

            // Create Async call state object
            StateInfo state = new StateInfo();
            state.DnsUpdateClient = _dnsOMaticClient;
            state.ResetEvent = _autoEvent;

            // Wire up timer delegate
            TimerCallback timerDelegate = new TimerCallback(this.timer_Tick);
            // Setup timer start immediately, and every 10 seconds after
            _timer = new Timer(timerDelegate, state, 0, _timerInterval);
            _watch.Start();

            Trace.WriteLine("Timer Started.");

            // Loop infinately to keep this thread up.  To release cycles, 
            // we wait (WaitOne) for the AutoReset event to signal (Set).
            while (true)
            {
                // Block infinately until the Timer event signals us (when it's finished)
                _autoEvent.WaitOne(-1, false);

                // Check for shutdown
                if (_shutDown)
                    break;
            }

        }

        protected override void OnStop()
        {
            try
            {
                Trace.WriteLine("Stopping DnsOMatic Submission Service...");
                // Stop the timer
                Trace.WriteLine("Disabling timer.");
                _timer.Change(Timeout.Infinite, Timeout.Infinite);

                // tell the thread to end
                _shutDown = true;
                _autoEvent.Set();


                _isRunning = false;
                Trace.WriteLine("Waiting for Log Submission Thread to end.");
                // Abort the log submission thread if it's still running...
                if (_updateOMaticThread.IsAlive)
                {
                    // Give the thread some time to finish if was giving the appropriate signal....
                    if (!_updateOMaticThread.Join(TimeSpan.FromSeconds(10)))
                    {
                        Trace.WriteLine("Thread hasn't responded in 10 ten seconds. Aborting.");
                        _updateOMaticThread.Abort();
                    }
                }
            }
            finally
            {
                _timer.Dispose();
            }
        }

        private void timer_Tick(Object dnsOMaticState)
        {
            Monitor.Enter(_dnsOMaticClient);
            StateInfo state = (StateInfo)dnsOMaticState;

            int submissionIntervalInMinutes = ((int)DnsOMaticConfiguration.Instance.Interval);
            TimeSpan span = TimeSpan.FromMinutes(submissionIntervalInMinutes);

            // is it time to run?
            // Trace.WriteLine("Elapsed Seconds: " + (_watch.ElapsedTicks / Stopwatch.Frequency));
            // Trace.WriteLine("Submission Interval Seconds: " + span.TotalSeconds);

            if (((_watch.ElapsedTicks / Stopwatch.Frequency) >= span.TotalSeconds) || _firstRun)
            {
                _firstRun = false;
                // reset timer counters
                _watch.Reset();
                _watch.Start();
                Trace.WriteLine("Configuration Submission Interval Timer elapsed.");

                // Do work

                string statusMessage;
                if (state.DnsUpdateClient.UpdateDnsOMatic(out statusMessage))
                {
                    Trace.WriteLine("SUCCESS: " + statusMessage);
                }
                else
                {
                    Trace.WriteLine("FAIL: " + statusMessage);
                }
            }

            Monitor.Exit(_dnsOMaticClient);
        }
    }
}
